/**
 * fshows.com
 * Copyright (C) 2013-2019 All Rights Reserved.
 */
package com.example.proxydemo.mycglib;


import com.example.proxydemo.mycglib.model.MethodInfoModel;
import com.example.proxydemo.mycglib.model.SourceProxyModel;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * 代理类源码生成工具
 *
 * @author Liluqing
 * @version GenerateSourceCode.java, v 0.1 2019-08-08 19:54
 */
public class GenerateSourceCodeUtil {

    /**
     * 生成代理类java源文件
     *
     * @param superClass 被代理类class
     * @return 代理类源文件file对象
     */
    public static SourceProxyModel generateCodeFile(Class<?> superClass) {
        String proxyClassName = superClass.getSimpleName() + "$Proxy$0";
        // 生成代理类源码
        String sourceStr = generateCodeStr(proxyClassName, superClass);
        // 输出源码文件磁盘
        File sourceFile = outFile(sourceStr, proxyClassName, superClass);

        SourceProxyModel sourceProxyModel = new SourceProxyModel();
        sourceProxyModel.setSourceFile(sourceFile);
        sourceProxyModel.setClassName(proxyClassName);
        sourceProxyModel.setFullClassName(superClass.getPackage().getName() + "." + proxyClassName);
        return sourceProxyModel;
    }


    /**
     * 生成代理类源码
     *
     * @param proxyClassName
     * @param superClass
     * @return
     */
    private static String generateCodeStr(String proxyClassName, Class<?> superClass) {
        // 获取方法信息
        List<MethodInfoModel> methodInfoModelList = new ArrayList<>();
        Method[] method = superClass.getDeclaredMethods();
        for (Method m : method) {
            if (Modifier.isFinal(m.getModifiers())) {
                continue;
            }
            if (!Modifier.isPublic(m.getModifiers())) {
                continue;
            }
            MethodInfoModel methodInfoModel = new MethodInfoModel();
            methodInfoModel.setName(m.getName());
            methodInfoModel.setReturnType(m.getReturnType().getName());
            List<String> paramTypes = new ArrayList<>();
            for (Class<?> c : m.getParameterTypes()) {
                paramTypes.add(c.getName());
            }
            methodInfoModel.setParamTypes(paramTypes);
            methodInfoModelList.add(methodInfoModel);
        }
        return renderTemplate(proxyClassName, superClass.getName(), superClass.getPackage().getName(), methodInfoModelList);
    }

    /**
     * 输出Java源文件到被代理类同路径下
     *
     * @param sourceStr
     * @param proxyClassName
     * @param superClass
     * @return
     */
    private static File outFile(String sourceStr, String proxyClassName, Class<?> superClass) {
        String filePath = superClass.getResource("").getPath()  + proxyClassName + ".java";
        File f = new File(filePath);
        if (f.exists()) {
            f.delete();
        }
        try(FileWriter fileWriter = new FileWriter(filePath)) {
            fileWriter.write(sourceStr);
            fileWriter.flush();
        }catch (IOException e) {
            e.printStackTrace();
        }
        return new File(filePath);
    }

    /**
     * 通过模版引擎读入原始类信息生成代理类源码
     *
     * @param className 代理类名称
     * @param superClass 被代理类class
     * @param methodInfoModelList 被代理类方法信息
     * @return 代理类源码
     */
    private static String renderTemplate(String className, String superClass, String packageName,List<MethodInfoModel> methodInfoModelList) {
        //初始化代码
        ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader();
        Configuration cfg = null;
        try {
            cfg = Configuration.defaultConfiguration();
        } catch (IOException e) {
            e.printStackTrace();
        }
        GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
        //获取模板
        Template t = gt.getTemplate("/proxyTemplate.vm");
        t.binding("methodInfoList", methodInfoModelList);
        t.binding("className", className);
        t.binding("superClass", superClass);
        t.binding("packageName", packageName);
        //渲染结果
        return t.render();
    }

}